﻿using System;
using System.Collections.Generic;
using Heaven.Engine.Concurrency;
using Heaven.Engine.Animation;
using Heaven.Engine.Transformations;
using Heaven.Geometry;

namespace Heaven.Engine
{
    /// <summary>
    /// Represents group of nodes. This class allows 
    /// to group nodes in one node. That provides us 
    /// ability to make common properties, 
    /// parent transformation and so on
    /// </summary>
    public class NodeGroup : Node, IEnumerable<Node>
    {        
        #region Events
       
        /// <summary>
        /// Occurs when IsHidden property has been changed
        /// </summary>
        //public event EventHandler IsHiddenChanged;

        /*void RaiseEvent(EventHandler handler)
        {
            if (handler != null) handler(this, EventArgs.Empty);
        }*/

        #endregion

        #region Fields
        
        // Is this node hidden
        bool hidden = false;
        // Is node attached to node collection?
        bool isAttached = false;

        // Internal collection of nodes
        List<Node> nodes = new List<Node>();
        
        #endregion

        #region Properties
               

        /// <summary>
        /// Is this node hidden
        /// </summary>
        public bool IsHidden
        {
            get { return hidden; }
            set 
            { 
                hidden = value;    
                // TODO: detach children??
                Invalidate();
            }
        }

        /// <summary>
        /// Is the node attached to a render graph?
        /// </summary>
        internal bool IsAttached
        {
            get { return parents.Count > 0; }
        }

        /// <summary>
        /// Gets the numbers of the nodes
        /// </summary>
        public int Count { get { return nodes.Count; } }

        /// <summary>
        /// Gets an node
        /// </summary>
        /// <param name="i">Index of the item</param>
        /// <returns>Node</returns>
        public Node this[int i]
        {
            get { return nodes[i]; }
        }
        
        #endregion

        #region Methods

        /// <summary>
        /// Adds an item
        /// </summary>
        /// <param name="item">An item</param>
        public void Add(Node item)
        {
            nodes.Add(item);
                        
            item.Attach(this);
            item.Invalidated += new EventHandler(OnNodeInvalidated);
            Invalidate();
        }

        /// <summary>
        /// Adds items
        /// </summary>
        /// <param name="items">Items</param>
        public void AddRange(IEnumerable<Node> items)
        {
            foreach (Node item in items) Add(item);
        }

        /// <summary>
        /// Removes the given item
        /// </summary>
        /// <param name="item">An item</param>
        public void Remove(Node item)
        {
            nodes.Remove(item);

            item.Dettach(this);
            item.Invalidated -= new EventHandler(OnNodeInvalidated);
            Invalidate();
        }

        /// <summary>
        /// Clears the collection
        /// </summary>
        public void Clear()
        {
            foreach (Node item in nodes)
            {
                item.Invalidated -= new EventHandler(OnNodeInvalidated);
                item.Dettach(this);
            }

            nodes.Clear();
            Invalidate();
        }
                
        #endregion

        #region Event's Handlers
        
        void OnNodeInvalidated(object sender, EventArgs e)
        {
            Invalidate();
        }

        #endregion

        #region Internals

        /// <summary>
        /// Attachs node
        /// </summary>
        /// <param name="node">Node</param>
        internal override void Attach(Node node)
        {
            base.Attach(node);           

            Invalidate();
        }

        /// <summary>
        /// Dettachs node
        /// </summary>
        /// <param name="node">Node</param>
        internal override void Dettach(Node node)
        {
            base.Dettach(node);

            
            Invalidate();
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Default constructor
        /// </summary>
        public NodeGroup()
        {
        }        

        #endregion

        #region Enumerations

        /// <summary>
        /// Enumerates nodes
        /// </summary>
        /// <returns>A System.Collections.Generic.IEnumerable<Visual> that 
        /// can be used to iterate through the collection</returns>
        internal override IEnumerable<Visual> Enumerate()
        {            
            foreach(Node node in nodes)
            {
                if(node is Visual) yield return (Visual)node;
                else foreach(Visual visual in node.Enumerate())
                {
                    yield return visual;
                }
            }
        }         

        /// <summary>
        /// Gets enumerator
        /// </summary>
        /// <returns>Enumerator</returns>
        public IEnumerator<Node> GetEnumerator()
        {
            return nodes.GetEnumerator();
        }


        /// <summary>
        /// Gets enumerator
        /// </summary>
        /// <returns>Enumerator</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return nodes.GetEnumerator();
        }

        #endregion
    }
}
